/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * oscalls.cc																*
 *																			*
 * Calls required by newlib are implemented in this code. Not to be linked	*
 * into the kernel.															*
 *																			*
\***************************************************************************/

// DON'T link us into the kernel (change to #if 1 when compiling with newlib)
#if 1

// includes
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <_ansi.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <reent.h>
#include "mmsg.h"
#include "newio.h"
#include "portio.h"

// we need to define our own version of errno
#undef errno
extern int errno;

// kills a program, no cleanup at all
void _exit()
{
	asm( "mov $0,%eax\nint $0x80" );
}

// closes a file
int close( int fd )
{
	return new_close( fd );
}

// environment variable list
char* env[1] = { 0 };
char** environ = env;

// transfers control to a new process
int execve( char* name, char** argv, char** env )
{
	errno = ENOMEM;
	return -1;
}

// creates a new process
int fork()
{
	errno = EAGAIN;
	return -1;
}

// gets the status of an open file
int fstat( int file, struct stat* st )
{
	return new_fstat( file, st );
}

// gets the current process id
int getpid()
{
	unsigned int ebx;
	asm( "mov $22,%%eax\nint $0x80" : "=b" (ebx) );
	return ebx;
}

// query whether an output stream is a terminal
int isatty( int fd )
{
	return 1;
}

// sends a signal to a process
int kill( int pid, int sig )
{
	errno = EINVAL;
	return -1;
}

// establishes a new name for an existing file
int link( char* old, char* newf )
{
	errno = EMLINK;
	return -1;
}

// seeks to a position in a file
int lseek( int file, int ptr, int dir )
{
	if( file >= 3 )
		return new_seek( file, ptr, dir );
	else
	{
		unsigned int ebx;
		asm( "int $0x80" : "=b" (ebx) : "a" (14), "b" (dir), "c" (file), "d" (ptr) );
		return ebx;
	}
}

// opens a file
int open( const char* name, int flags, ... )
{
	va_list vl;
	va_start(vl,flags);
	int mode = va_arg( vl, int );
	va_end(vl);
	return new_open( name, flags, mode );
}

// reads from a file
int read( int fd, char* ptr, int len )
{
	if( fd < 3 )
	{
		int d = new_open( "/dev/tty", 0, O_RDONLY );
		int l = new_read( d, ptr, len );
		new_close( d );
		return l;
	}
	else
	{
		return new_read( fd, ptr, len );
	}
}

caddr_t sbrk( int incr )
{
	// the new end of the memory
	unsigned int newend = 0;
	asm( "int $0x80" : "=b" (newend) : "a" (26), "c" (incr) );
	
	// return it
	return (caddr_t) newend;
}

// gets the status of a file
int _stat( char* file, struct stat* st )
{
	st->st_mode = S_IFCHR;
	return 0;
}

// gets timing information for the current process
clock_t times( struct tms* buf )
{
	unsigned int ebx;
	asm( "int $0x80" : "=b" (ebx) : "a" (24), "D" ((unsigned int) buf) );
	return ebx;
}

// gets the current time of day
int gettimeofday( struct timeval* tm, struct timezone* tzp )
{
	// real time clock ports
	#define RTC_BASE 0x70
	#define RTC_DATA 0x71
	
	// wait until we can read the data
	int waitc = 1;
	while( waitc )
	{
		// update in progress?
		mlib_outportb( RTC_BASE, 0xA );
		
		// get the data
		unsigned char c = mlib_inportb( RTC_DATA );
		
		// check
		if( ! ( c & 0x80 ) )
		{
			// done
			waitc = 0;
		}
	}
	
	// seconds
	mlib_outportb( RTC_BASE, 0 );
	unsigned char sec = mlib_inportb( RTC_DATA );
	
	// minutes
	mlib_outportb( RTC_BASE, 2 );
	unsigned char min = mlib_inportb( RTC_DATA );
	
	// hours
	mlib_outportb( RTC_BASE, 4 );
	unsigned char hour = mlib_inportb( RTC_DATA );
	
	// day
	mlib_outportb( RTC_BASE, 7 );
	unsigned char day = mlib_inportb( RTC_DATA );
	
	// month
	mlib_outportb( RTC_BASE, 8 );
	unsigned char month = mlib_inportb( RTC_DATA );

	// year (only 2 digits, between 2000-2099)
	mlib_outportb( RTC_BASE, 9 );
	unsigned char year = mlib_inportb( RTC_DATA );
	
	// how do we deal with the data?
	mlib_outportb( RTC_BASE, 0xB );
	unsigned char format = mlib_inportb( RTC_DATA );
	
	// convert all fields from BDC to binary if bit is clear
	if( ! ( format & 0x4 ) )
	{
		// convert
		sec = ( sec & 0xF ) + ( sec >> 4 ) * 10;
		min = ( min & 0xF ) + ( min >> 4 ) * 10;
		hour = ( hour & 0xF ) + ( ( hour & 0x70 ) >> 4 ) * 10 + ( hour & 0x80 );
		day = ( day & 0xF ) + ( day >> 4 ) * 10;
		month = ( month & 0xF ) + ( month >> 4 ) * 10;
		year = ( year & 0xF ) + ( year >> 4 ) * 10;
	}
	
	// convert hours --> 24 format
	if( ! ( format & 0x2 ) )
	{
		// am/pm?
		int ampm = hour & 0x80;
		
		// and the bits
		hour &= 0x7F;
		
		// fix 24:00 = 12am oddity
		if( hour == 12 ) hour = 0;
		if( ampm ) hour += 12;
	}
	
	// don't take this into the past before 2000, it won't work
	// also don't take into future, past 2100 (by which time I'll be dead), same reason
	
	// days since 2000
	unsigned int fullyearsdays = year * 365 + ( ( year - 1 ) / 4 ) + 1;
	
	// offset of days for each month
	static unsigned int dayoffset[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
	
	// adjust day for leap year
	unsigned int thisyeardays = dayoffset[month-1] + day - ((( year % 4 ) == 0 && month > 2 ) ? 0 : 1 ); // only if no leap day passed
	
	// sum days before and during this year to get date, adjust for unix epoch
	unsigned int date = fullyearsdays + thisyeardays + 10957; // 10957 days from 1970 to 2000
	
	// get the time now - seconds past the epoch
	time_t time = sec + 60 * (min + 60 * ( hour + 24 * date ) );
	
	// ok, now we set the structure contents
	tm->tv_sec = time;
	tm->tv_usec = 0;
	
	// no error code is ever returned
	return 0;
}

// removes a file directory entry
int unlink( char* name )
{
	errno = ENOENT;
	return -1;
}

// wait for a child process
int wait( int* status )
{
	errno = ECHILD;
	return -1;
}

// writes to a file
int write( int fd, char* ptr, int len )
{
	if( fd < 3 )
	{
		int d = new_open( "/dev/tty", 0, O_WRONLY );
		int l = new_write( d, ptr, len );
		new_close( d );
		return l;
	}
	else
	{
		return new_write( fd, ptr, len );
	}
}

#endif
